﻿using System;
using System.Collections.Generic;
using System.Text;

namespace Microsoft.Xna.Framework
{
	public class FactCubeCamera
	{
		/// <summary>
		/// Create a View matrix from an existing ProjectionCamera
		/// (code acquired with Reflector)
		/// </summary>
        /// <param name="position">Camera position</param>
        /// <param name="lookDirection">Look direction</param>
        /// <param name="upDirection">Up direction</param>
		/// <returns>View matrix</returns>
		public static Matrix CreateViewMatrix( Vector3 position, Vector3 lookDirection, Vector3 upDirection )
		{
			Vector3 vectord2 = -lookDirection;
			vectord2.Normalize();
			Vector3 vectord3 = Vector3.Cross( upDirection, vectord2 );
			vectord3.Normalize();
			Vector3 vectord4 = Vector3.Cross( vectord2, vectord3 );
			Vector3 vectord = position;
			float offsetX = -Vector3.Dot( vectord3, vectord );
			float offsetY = -Vector3.Dot( vectord4, vectord );
			float offsetZ = -Vector3.Dot( vectord2, vectord );

			return new Matrix(
                       vectord3.X, vectord4.X, vectord2.X, 0,
                       vectord3.Y, vectord4.Y, vectord2.Y, 0,
                       vectord3.Z, vectord4.Z, vectord2.Z, 0,
                       offsetX, offsetY, offsetZ, 1 );
		}

		/// <summary>
		/// Create a Projection matrix from an existing ProjectionCamera and view size
		/// (code acquired with Reflector)
		/// </summary>
        /// <param name="near">Near plane distance</param>
        /// <param name="far">Far plane distance</param>
        /// <param name="fov">Field of view</param>
		/// <param name="width">Width</param>
		/// <param name="height">Height</param>
        public static Matrix CreateProjectionMatrix( float width, float height, float fov, float near, float far )
		{
			float aspectRatio = width / height;
			float zn = near;
			float zf = far;

			float num2 = (float)Math.Tan( DegreesToRadians( fov ) / 2 );
			float num5 = aspectRatio / num2;
			float num4 = 1 / num2;
			float num = ( zf != double.PositiveInfinity ) ? ( zf / ( zn - zf ) ) : -1;
			return new Matrix( num4, 0, 0, 0, 0, num5, 0, 0, 0, 0, num, -1, 0, 0, zn * num, 0 );
		}

		/// <summary>
		/// Get display matrix
		/// </summary>
		/// <param name="camera">Camera</param>
		/// <param name="width">Viewport width</param>
		/// <param name="height">Viewport height</param>
		/// <returns>Display matrix</returns>
		public static Matrix GetDisplayMatrix( Matrix view, Matrix projection, float width, float height )
		{
			return ( view * projection ) * GetHomogeneousToViewportTransform3D( width, height );
		}

		#region Internal methods

		internal static double DegreesToRadians( double degrees )
		{
			return ( degrees * 0.017453292519943295 );
		}

		public static Matrix GetHomogeneousToViewportTransform3D( float width, float height )
		{
			float halfWidth = width / 2;
			float halfHeight = height / 2;
			return new Matrix( halfWidth, 0, 0, 0, 0, -halfHeight, 0, 0, 0, 0, 1, 0, halfWidth, halfHeight, 0, 1 );
		}

		#endregion
	}
}